package ar.edu.utn.frba.tacs.warnes.utils;

import java.util.ArrayList;
import java.util.List;

import ar.edu.utn.frba.tacs.warnes.cotizacion.Moneda;
import ar.edu.utn.frba.tacs.warnes.cotizacion.Peso;
import ar.edu.utn.frba.tacs.warnes.entities.Auto;
import ar.edu.utn.frba.tacs.warnes.entities.Pedido;
import ar.edu.utn.frba.tacs.warnes.entities.Pieza;
import ar.edu.utn.frba.tacs.warnes.entities.Reserva;
import ar.edu.utn.frba.tacs.warnes.entities.Venta;
import ar.edu.utn.frba.tacs.warnes.pieza.PiezaDisponibleState;
import ar.edu.utn.frba.tacs.warnes.utils.ConstantesWarnes.Categoria;
import ar.edu.utn.frba.tacs.warnes.utils.ConstantesWarnes.PiezaState;

public class ContenedorWarnes {

	private static ContenedorWarnes instancia;
	private List<Pieza> piezas = new ArrayList<Pieza>();
	private List<Auto> autos = new ArrayList<Auto>();
	private List<Reserva> reservas = new ArrayList<Reserva>();
	private List<Venta> ventas = new ArrayList<Venta>();
	private List<Pedido> pedidos = new ArrayList<Pedido>();
	private Moneda moneda;

	public static ContenedorWarnes getInstance() {
		if (instancia == null) {
			instancia = new ContenedorWarnes();
		}
		return instancia;
	}

	private ContenedorWarnes() {
		this.setMoneda(Peso.getInstance());
	}

	public List<Pedido> getPedidos() {
		return this.pedidos;
	}

	public void setPedidos(List<Pedido> pedidos) {
		this.pedidos = pedidos;
	}

	public void setPiezas(List<Pieza> piezas) {
		this.piezas = piezas;
	}

	public List<Pieza> getPiezas() {
		return this.piezas;
	}

	private void addPieza(Pieza pieza) {
		if (this.getPiezas() == null) {
			this.setPiezas(new ArrayList<Pieza>());
		}
		this.getPiezas().add(pieza);
	}

	/**
	 * Se comporta como una Factory de Piezas. Crea una nueva Pieza, la cual
	 * agrega a su coleccion correspondiente. La pieza nueva va a tener un
	 * estado <b>DISPONIBLE</b> (el seteo del id a la Pieza es temporal, despues
	 * de esto se va a encargar Hibernate)
	 * 
	 * @return Pieza
	 */
	public Pieza creaUnaPieza() {
		return this.creaUnaPieza("");
	}

	/**
	 * Crea una pieza con descripcion
	 * 
	 * @param descripcion
	 * @return Pieza
	 */
	public Pieza creaUnaPieza(String descripcion) {
		Pieza pieza = new Pieza(descripcion);
		this.addPieza(pieza);
		pieza.cambiarEstado(PiezaDisponibleState.getInstance());
		pieza.setId(new Long(this.getPiezas().size() + 1));
		return pieza;
	}

	/**
	 * Esta consulta devuelve las Piazas para una categoria dada.
	 * 
	 * @param categoria
	 * @return List<Pieza>
	 */
	public List<Pieza> getPiezas(Categoria categoria) {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (categoria.equals(pieza.getCategoria())) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	public void setAutos(List<Auto> autos) {
		this.autos = autos;
	}

	public List<Auto> getAutos() {
		return this.autos;
	}

	private void addAuto(Auto auto) {
		if (this.getAutos() == null) {
			this.setAutos(new ArrayList<Auto>());
		}
		this.getAutos().add(auto);
	}

	/**
	 * Esta consulta devuelve los autos para una patente dada.
	 * 
	 * @param patente
	 * @return List<Auto>
	 */
	public List<Auto> getAutos(String patente) {
		List<Auto> autosBuscados = new ArrayList<Auto>();
		for (Auto auto : this.autos) {
			if (patente.equals(auto.getPatente())) {
				autosBuscados.add(auto);
			}
		}
		return autosBuscados;
	}

	/**
	 * Se comporta como una Factory de Autos. Crea un nuevo Auto, la cual agrega
	 * a su coleccion correspondiente.
	 * 
	 * @return Auto
	 */
	public Auto creaUnAuto() {
		return this.creaUnAuto("");
	}

	/**
	 * Crea un nuevo Auto con patente.
	 * 
	 * @return Auto
	 */
	public Auto creaUnAuto(String patente) {
		Auto auto = new Auto(patente);
		this.addAuto(auto);
		return auto;
	}

	/**
	 * Vacia la coleccion de Piezas y de Autos del Contenedor.
	 */
	public void limpiate() {
		this.setPiezas(new ArrayList<Pieza>());
		this.setAutos(new ArrayList<Auto>());
	}

	/**
	 * Esta consulta devuelve las Piazas que esten en un estado determinado.
	 * 
	 * @param estado
	 * @return List<Pieza>
	 */
	public List<Pieza> getPiezas(PiezaState state) {
		List<Pieza> piezas;
		switch (state.getValue()) {
		case ConstantesWarnes.PIEZA_DISPONIBLE:
			piezas = this.getPiezasDisponibles();
			break;
		case ConstantesWarnes.PIEZA_RESERVADA:
			piezas = this.getPiezasReservada();
			break;
		case ConstantesWarnes.PIEZA_VENDIDA:
			piezas = this.getPiezasVendidas();
			break;
		default:
			piezas = new ArrayList<Pieza>();
			break;
		}
		return piezas;
	}

	private List<Pieza> getPiezasDisponibles() {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (pieza.estaDisponible()) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	private List<Pieza> getPiezasReservada() {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (pieza.estaReservada()) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	private List<Pieza> getPiezasVendidas() {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (pieza.estaVendida()) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	/**
	 * Esta consulta devuelve una coleccion con 0 o 1 piezas segun el id
	 * indicado. Devuelve una lista para no romper el polimorfismo con los demas
	 * getPiezas()
	 * 
	 * @param id
	 * @return List<Pieza> con 0 o 1 elemento
	 */
	public List<Pieza> getPiezas(Long id) {
		List<Pieza> piezasBuscadas = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (id.equals(pieza.getId())) {
				piezasBuscadas.add(pieza);
			}
		}
		return piezasBuscadas;
	}

	/**
	 * Esta consulta devuelve una coleccion con 0 o 1 autos segun el id
	 * indicado. Devuelve una lista para no romper el polimorfismo con los demas
	 * getAutos()
	 * 
	 * @param id
	 * @return List<Pieza> con 0 o 1 elemento
	 */
	public List<Auto> getAutos(Long id) {
		List<Auto> autosBuscadas = new ArrayList<Auto>();
		for (Auto auto : this.autos) {
			if (id.equals(auto.getId())) {
				autosBuscadas.add(auto);
			}
		}
		return autosBuscadas;
	}

	/**
	 * Se comporta como una Factory de Reservas. Crea una nueva Reserva, la cual
	 * agrega a su coleccion correspondiente.
	 * 
	 * @return Reserva
	 */
	public Reserva nuevaReserva() {
		Reserva res = new Reserva();
		this.addReserva(res);
		return res;
	}

	public void setReservas(List<Reserva> reservas) {
		this.reservas = reservas;
	}

	public List<Reserva> getReservas() {
		return this.reservas;
	}

	/**
	 * Agrega una reserva al sistema, la cual contendra las piezas que se
	 * quieren reservar
	 * 
	 * @param res
	 */
	private void addReserva(Reserva res) {
		if (this.getReservas() == null) {
			this.setReservas(new ArrayList<Reserva>());
		}
		this.getReservas().add(res);
	}

	public Venta nuevaVenta() {
		Venta ven = new Venta();
		this.addVenta(ven);
		return ven;
	}

	public void setVenta(List<Venta> venta) {
		this.ventas = venta;
	}

	public List<Venta> getVenta() {
		return this.ventas;
	}

	/**
	 * Agrega una venta al sistema, la cual contendra las piezas que se quieren
	 * vender
	 * 
	 * @param res
	 */
	private void addVenta(Venta ven) {
		if (this.getVenta() == null) {
			this.setVenta(new ArrayList<Venta>());
		}
		this.getVenta().add(ven);
	}

	/**
	 * Se comporta como una Factory de Pedidos. Crea un nuevo Pedido, la cual
	 * agrega a su correspondiente coleccion.
	 * 
	 * @return Pedido
	 */
	public Pedido crearUnPedido(List<Pieza> piezas) {
		Pedido pedido = new Pedido(piezas);
		this.addPedido(pedido);
		return pedido;
	}

	/**
	 * Agrega un pedido al sistema, la cual contendra las piezas que se quieren
	 * reservar
	 * 
	 * @param res
	 */
	private void addPedido(Pedido ped) {
		if (this.getPedidos() == null) {
			this.setPedidos(new ArrayList<Pedido>());
		}
		this.getPedidos().add(ped);
	}

	public boolean tenesLaPiezaConId(Long id) {
		boolean resultado = false;
		for (Pieza pieza : this.getPiezas()) {
			if (new Long(pieza.getId()).intValue() == id.intValue()) {
				resultado = true;
				break;
			}
		}
		return resultado;
	}

	/**
	 * Pide al cotizador el simbolo a mostrar segun la moneda que se esta
	 * utilizando
	 * 
	 * @return String simbolo
	 */
	public String getSimboloMoneda() {
		return this.getMoneda().getSimbolo();
	}

	/**
	 * Cuando desde la pagina borro una pieza, debo borrar la pieza del auto y
	 * del pedido
	 */
	public void borrarPieza(Pieza pieza) {
		for (Auto auto : this.getAutos()) {
			auto.borrarPieza(pieza);
		}
		for (Pedido pedido : this.getPedidos()) {
			pedido.borrarPieza(pieza);
		}
		this.getPiezas().remove(pieza);
	}

	/**
	 * Cuando desde la pagina borro un auto, debo borrarlo de la pieza
	 */
	public void borrarAuto(Auto auto) {
		List<Pieza> listaAutos = new ArrayList<Pieza>(auto.getPiezasDelAuto());
		for (Pieza pieza : listaAutos) {
			pieza.setAutoOrigen(null);
		}
		this.getAutos().remove(auto);
	}

	public List<Pieza> getPiezas(List<Pieza> piezasDelPedido) {
		List<Pieza> piezas = new ArrayList<Pieza>();
		for (int i = 0; i < piezasDelPedido.size(); i++) {
			for (Pieza pieza : this.getPiezas()) {
				if (pieza.getDescripcion().equals(piezasDelPedido.get(i))) {
					piezas.add(pieza);
					break;
				}
			}
		}
		return piezas;
	}

	/**
	 * Cuando desde la pagina borro un pedido, debo disponibilizar todas sus
	 * piezas
	 */
	public void borrarPedido(Pedido pedido) {
		for (Pieza pieza : pedido.getPiezas()) {
			pieza.disponibilizate();
		}
		this.getPedidos().remove(pedido);
	}

	public Pedido getPedido(long id) {
		Pedido pedido = null;
		for (Pedido p : ContenedorWarnes.getInstance().getPedidos())
			if (p.getId() != null && id == p.getId().longValue())
				pedido = p;
		return pedido;
	}

	/**
	 * Setea el tipo de moneda con el que se trabajara. Usa un singleton de
	 * CotizadorMoneda que maneja todo el tema monetario.
	 * 
	 * @param tipoMoneda
	 */
	public void setMoneda(Moneda tipoMoneda) {
		this.moneda = tipoMoneda;
	}

	/**
	 * Pide a CotizadorMoneda el valor/cotizacion de la moneda con la que se
	 * esta trabajando
	 * 
	 * @return
	 */
	public Float getValorMoneda() {
		return this.getMoneda().getCotizacion();
	}

	/**
	 * Cambia la cotizacion de la moneda indicada.
	 * 
	 * @param tipoMoneda
	 * @param cotizacion
	 */
	public void cambiarCotizacion(Moneda tipoMoneda, Float cotizacion) {
		tipoMoneda.setCotizacion(cotizacion);
	}

	/**
	 * Devuelve la moneda en uso
	 * @return
	 */
	public Moneda getMoneda() {
		return this.moneda;
	}

	public Auto getAuto(Long id) {
		List<Auto> autos = this.getAutos(id);
		return autos.isEmpty() ? null : autos.get(0);
	}

	public void borrarAuto(Long id) {
		Auto auto = this.getAuto(id);
		this.borrarAuto(auto);
	}

	public Pieza getPieza(Long id) {
		List<Pieza> pieza = this.getPiezas(id);
		return pieza.isEmpty() ? null : pieza.get(0);
	}

	public void borrarPedido(Long id) {
		Pedido ped = this.getPedido(id);
		this.borrarPedido(ped);
	}

	public void borrarPieza(Long id) {
		Pieza pie = this.getPieza(id);
		this.borrarPieza(pie);
	}

	public void borrarAuto(String patente) {
		Auto auto = this.getAuto(patente);
		this.borrarAuto(auto);
	}

	public Auto getAuto(String patente) {
		List<Auto> autos = this.getAutos(patente);
		return autos.isEmpty() ? null : autos.get(0);
	}

	/**
	 * Devuelve la 1ra pieza que matchee con la descripcion
	 * @param descripcion
	 * @return
	 */
	public Pieza getPieza(String descripcion) {
		List<Pieza> pieza = this.getPiezas(descripcion);
		return pieza.isEmpty() ? null : pieza.get(0);
	}
	
	/**
	 * Esta consulta devuelve los autos para una patente dada.
	 * 
	 * @param patente
	 * @return List<Auto>
	 */
	public List<Pieza> getPiezas(String descripcion) {
		List<Pieza> piezasBuscados = new ArrayList<Pieza>();
		for (Pieza pieza : this.piezas) {
			if (descripcion.equals(pieza.getDescripcion())) {
				piezasBuscados.add(pieza);
			}
		}
		return piezasBuscados;
	}

	public void borrarPieza(String descripcion) {
		Pieza pieza = this.getPieza(descripcion);
		this.borrarPieza(pieza);
	}
}
